"""
市场数据服务
负责从多个数据源获取股票数据，并提供数据管理功能
"""

import os
import pandas as pd
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional
from decimal import Decimal

# 导入LongPort API
from longport.openapi import QuoteContext, Config, Period, AdjustType

# 导入数据库服务
try:
    from database.service import db_service
    DATABASE_AVAILABLE = True
except ImportError:
    db_service = None
    DATABASE_AVAILABLE = False

logger = logging.getLogger(__name__)


class MarketDataService:
    """
    市场数据服务类
    提供统一的市场数据访问接口，支持多数据源
    """
    
    def __init__(self):
        """初始化市场数据服务"""
        self._setup_longport_client()
        
    def _setup_longport_client(self):
        """设置长桥API客户端"""
        try:
            # 从环境变量获取LongPort配置
            self.app_key = os.getenv('LONGPORT_APP_KEY')
            self.app_secret = os.getenv('LONGPORT_APP_SECRET')
            self.access_token = os.getenv('LONGPORT_ACCESS_TOKEN')
            
            if not all([self.app_key, self.app_secret, self.access_token]):
                logger.warning("LongPort API配置不完整，部分功能可能受限")
                self.longport_available = False
                return
            
            # 初始化LongPort客户端
            self.config = Config(
                app_key=self.app_key,
                app_secret=self.app_secret,
                access_token=self.access_token
            )
            self.longport_available = True
            logger.info("LongPort API客户端初始化成功")
            
        except Exception as e:
            logger.error(f"LongPort API客户端初始化失败: {e}")
            self.longport_available = False
    
    async def get_stock_data(self, symbol: str, period_days: int = 430) -> pd.DataFrame:
        """
        获取股票历史数据
        
        优先级：数据库 -> LongPort API + 自动计算并保存
        
        Args:
            symbol: 股票代码
            period_days: 获取天数
            
        Returns:
            包含OHLCV数据的DataFrame，列名为['Open', 'High', 'Low', 'Close', 'Volume']
        """
        try:
            # 第一优先级：从数据库获取数据
            if DATABASE_AVAILABLE and db_service:
                logger.debug(f"尝试从数据库获取 {symbol} 的数据")
                df = await self._get_data_from_database(symbol, period_days)
                
                # 只要有数据就使用，不再严格要求数据量
                if not df.empty:
                    logger.info(f"从数据库成功获取 {symbol} 的数据，共 {len(df)} 天")
                    return df
                else:
                    logger.warning(f"数据库中 {symbol} 没有数据，尝试API获取并计算")
            
            # 第二优先级：从LongPort API获取数据并自动计算保存
            if self.longport_available:
                logger.debug(f"尝试从LongPort API获取 {symbol} 的数据")
                df = await self._get_data_from_longport(symbol, period_days)
                
                if not df.empty:
                    logger.info(f"从LongPort API成功获取 {symbol} 的数据，共 {len(df)} 天")
                    
                    # 自动计算恐贪指标并保存到数据库
                    try:
                        await self._auto_calculate_and_save_fear_greed(symbol, df)
                        logger.info(f"✅ 成功将 {symbol} 的数据和计算结果保存到数据库")
                    except Exception as save_error:
                        logger.warning(f"保存 {symbol} 数据到数据库失败: {save_error}")
                    
                    return df
                else:
                    logger.warning(f"LongPort API未返回 {symbol} 的数据")
            
            # 没有可用数据源
            logger.error(f"无法获取 {symbol} 的数据")
            return pd.DataFrame()
            
        except Exception as e:
            logger.error(f"获取 {symbol} 数据失败: {e}")
            return pd.DataFrame()
    
    async def _get_data_from_database(self, symbol: str, period_days: int) -> pd.DataFrame:
        """从数据库获取股票数据"""
        try:
            df = await db_service.get_stock_data_as_dataframe(symbol, period_days)
            
            if not df.empty:
                logger.debug(f"数据库返回 {len(df)} 条记录")
                logger.debug(f"数据范围: {df.index[0]} 至 {df.index[-1]}")
                logger.debug(f"最新价格: {df['Close'].iloc[-1]}")
            
            return df
            
        except Exception as e:
            logger.error(f"从数据库获取数据失败: {e}")
            return pd.DataFrame()
    
    async def _get_data_from_longport(self, symbol: str, period_days: int) -> pd.DataFrame:
        """从LongPort API获取股票数据"""
        try:
            ctx = QuoteContext(self.config)
            
            # 获取日线数据 - 使用前复权数据
            candlesticks = ctx.candlesticks(
                symbol=symbol,
                period=Period.Day,
                count=period_days,
                adjust_type=AdjustType.ForwardAdjust
            )
            
            if not candlesticks:
                logger.warning(f"LongPort API未返回 {symbol} 的数据")
                return pd.DataFrame()
            
            # 转换为DataFrame
            df = self._convert_candlesticks_to_dataframe(candlesticks)
            
            logger.debug(f"LongPort API返回 {len(df)} 条记录")
            if not df.empty:
                logger.debug(f"数据范围: {df.index[0]} 至 {df.index[-1]}")
                logger.debug(f"最新价格: {df['Close'].iloc[-1]}")
            
            return df
            
        except Exception as e:
            logger.error(f"从LongPort API获取数据失败: {e}")
            return pd.DataFrame()
    
    def _convert_candlesticks_to_dataframe(self, candlesticks) -> pd.DataFrame:
        """将K线数据转换为DataFrame"""
        try:
            data = []
            for candle in candlesticks:
                # 安全地转换数值，确保类型正确
                def safe_float(value, default=None):
                    if value is None:
                        return default
                    try:
                        # 处理Decimal类型
                        if hasattr(value, '__float__'):
                            return float(value)
                        return float(value)
                    except (ValueError, TypeError):
                        return default
                
                def safe_int(value, default=None):
                    if value is None:
                        return default
                    try:
                        # 处理Decimal类型
                        if hasattr(value, '__int__'):
                            return int(value)
                        return int(float(value))
                    except (ValueError, TypeError):
                        return default
                
                data.append({
                    'date': candle.timestamp,
                    'Open': safe_float(candle.open),
                    'High': safe_float(candle.high),
                    'Low': safe_float(candle.low),
                    'Close': safe_float(candle.close),
                    'Volume': safe_int(candle.volume),
                    'Turnover': safe_float(candle.turnover)
                })
            
            df = pd.DataFrame(data)
            if not df.empty:
                df['date'] = pd.to_datetime(df['date'])
                df.set_index('date', inplace=True)
                df.sort_index(inplace=True)
                
                # 确保数值列的类型正确
                numeric_columns = ['Open', 'High', 'Low', 'Close', 'Turnover']
                for col in numeric_columns:
                    if col in df.columns:
                        df[col] = pd.to_numeric(df[col], errors='coerce')
                
                if 'Volume' in df.columns:
                    df['Volume'] = pd.to_numeric(df['Volume'], errors='coerce').fillna(0).astype(int)
            
            return df
            
        except Exception as e:
            logger.error(f"转换K线数据失败: {e}")
            return pd.DataFrame()
    

    
    async def _auto_calculate_and_save_fear_greed(self, symbol: str, df: pd.DataFrame):
        """自动计算恐贪指标并保存到数据库"""
        try:
            # 导入恐贪指标处理器
            from .fear_greed_processor import fear_greed_processor
            
            # 计算恐贪指标
            calculation_result = fear_greed_processor.calculate_fear_greed_index_from_data(df, symbol)
            
            # 保存到数据库（包含K线数据和恐贪指标）
            await self.save_stock_data_to_database(symbol, df, calculation_result)
            
            logger.info(f"✅ 自动计算并保存 {symbol} 的恐贪指标: {calculation_result.get('fear_greed_index', 'N/A')}")
            
        except Exception as e:
            logger.error(f"自动计算 {symbol} 恐贪指标失败: {e}")
            # 即使计算失败，也要保存K线数据
            await self.save_stock_data_to_database(symbol, df)
    
    async def ensure_data_completeness(self, symbol: str, period_days: int = 430) -> bool:
        """
        确保数据完整性
        
        修改为更宽松的检查：只要有数据就认为完整
        因为数据由服务端返回，后续会通过后台定时更新补充
        """
        try:
            logger.info(f"检查 {symbol} 数据完整性")
            
            # 检查数据库中是否有数据
            if DATABASE_AVAILABLE and db_service:
                df = await self._get_data_from_database(symbol, period_days)
                
                # 只要有数据就认为完整
                if not df.empty:
                    logger.info(f"✅ {symbol} 数据完整，有 {len(df)} 条记录")
                    return True
                else:
                    logger.warning(f"数据库中 {symbol} 没有数据")
            
            # 如果数据库没有数据，尝试从API获取
            if self.longport_available:
                logger.info(f"尝试从API获取 {symbol} 数据")
                df = await self._get_data_from_longport(symbol, period_days)
                
                if not df.empty:
                    # 保存到数据库
                    try:
                        await self.save_stock_data_to_database(symbol, df)
                        logger.info(f"✅ 成功保存 {symbol} 数据到数据库")
                        return True
                    except Exception as save_error:
                        logger.error(f"保存 {symbol} 数据失败: {save_error}")
                        return False
                else:
                    logger.warning(f"API未返回 {symbol} 数据")
            
            logger.warning(f"无法确保 {symbol} 数据完整性")
            return False
            
        except Exception as e:
            logger.error(f"检查 {symbol} 数据完整性失败: {e}")
            return False
    
    def _is_data_sufficient(self, df: pd.DataFrame, period_days: int) -> bool:
        """
        检查数据是否足够
        
        修改为更宽松的检查：只要有数据就认为足够
        因为数据由服务端返回，后续会通过后台定时更新补充
        """
        if df.empty:
            return False
        
        # 只要有数据就认为足够，不再严格要求数据量
        # 后续会通过后台定时更新来补充和更新数据
        logger.debug(f"数据充足性检查: 有 {len(df)} 条记录，认为足够")
        return len(df) > 0
    
    async def get_fear_greed_history(self, symbol: str, days: int = 430) -> List[Dict]:
        """
        获取恐贪指标历史数据
        
        优先级：数据库 -> 自动计算并保存
        
        Args:
            symbol: 股票代码
            days: 获取天数
            
        Returns:
            历史恐贪指标列表
        """
        try:
            # 优先从数据库获取恐贪指数历史数据
            if DATABASE_AVAILABLE and db_service:
                logger.debug(f"尝试从数据库获取 {symbol} 的恐贪指数历史数据")
                db_history = await db_service.get_fear_greed_history(symbol, days)
                
                logger.info(f"数据库查询结果: {symbol} 获取到 {len(db_history) if db_history else 0} 条记录")
                
                # 降低数据要求：只要有数据就使用
                if db_history and len(db_history) > 0:
                    # 转换为API格式，确保字段匹配
                    result_data = []
                    for item in db_history:
                        try:
                            api_data = item.to_api_format()
                            logger.debug(f"转换数据: {symbol} {item.timestamp} fear_greed_index={api_data.get('fear_greed_index')}")
                            
                            # 确保前端需要的字段都存在
                            formatted_data = {
                                'symbol': api_data.get('symbol'),
                                'timestamp': api_data.get('timestamp'),
                                'fear_greed_index': api_data.get('fear_greed_index'),
                                'sentiment': api_data.get('sentiment'),
                                'investment_advice': api_data.get('investment_advice'),
                                'close': api_data.get('close'),
                                'rsi': api_data.get('rsi'),
                                'volatility': api_data.get('volatility'),
                                'volume_ratio': api_data.get('volume_ratio'),
                                'momentum_score': api_data.get('momentum_score'),
                                'volatility_score': api_data.get('volatility_score'),
                                'volume_score': api_data.get('volume_score'),
                                'put_call_score': api_data.get('put_call_score')
                            }
                            result_data.append(formatted_data)
                        except Exception as convert_error:
                            logger.error(f"转换数据失败: {symbol} {item.timestamp} - {convert_error}")
                            continue
                    
                    logger.info(f"从数据库成功获取 {symbol} 的恐贪指数历史数据，共 {len(result_data)} 条记录")
                    return result_data
                else:
                    logger.warning(f"数据库中 {symbol} 的恐贪指数数据不足，尝试自动计算")
            else:
                logger.debug("数据库服务不可用")
            
            # 如果数据库没有数据，尝试自动计算并保存
            try:
                logger.info(f"开始自动计算 {symbol} 的恐贪指标历史数据")
                from .fear_greed_service import fear_greed_service
                history_data = await fear_greed_service._calculate_fear_greed_history(symbol, days)
                
                if history_data:
                    # 保存到数据库
                    await fear_greed_service._save_fear_greed_history_to_database(symbol, history_data)
                    logger.info(f"✅ 成功计算并保存 {symbol} 的恐贪指标历史数据")
                    
                    # 重新从数据库获取格式化数据
                    if DATABASE_AVAILABLE and db_service:
                        db_history = await db_service.get_fear_greed_history(symbol, days)
                        if db_history and len(db_history) > 0:
                            result_data = []
                            for item in db_history:
                                try:
                                    api_data = item.to_api_format()
                                    formatted_data = {
                                        'symbol': api_data.get('symbol'),
                                        'timestamp': api_data.get('timestamp'),
                                        'fear_greed_index': api_data.get('fear_greed_index'),
                                        'sentiment': api_data.get('sentiment'),
                                        'investment_advice': api_data.get('investment_advice'),
                                        'close': api_data.get('close'),
                                        'rsi': api_data.get('rsi'),
                                        'volatility': api_data.get('volatility'),
                                        'volume_ratio': api_data.get('volume_ratio'),
                                        'momentum_score': api_data.get('momentum_score'),
                                        'volatility_score': api_data.get('volatility_score'),
                                        'volume_score': api_data.get('volume_score'),
                                        'put_call_score': api_data.get('put_call_score')
                                    }
                                    result_data.append(formatted_data)
                                except Exception as convert_error:
                                    logger.error(f"转换数据失败: {symbol} {item.timestamp} - {convert_error}")
                                    continue
                            return result_data
                
                # 如果数据库获取失败，返回计算的数据
                return history_data
                
            except Exception as calc_error:
                logger.error(f"自动计算 {symbol} 恐贪指标历史数据失败: {calc_error}")
            
            # 没有可用数据源
            return []
            
        except Exception as e:
            logger.error(f"获取恐贪指标历史数据失败: {str(e)}")
            return []
    
    async def get_multiple_stocks_data(self, symbols: List[str], period_days: int = 430) -> Dict[str, pd.DataFrame]:
        """
        批量获取多个股票的数据
        
        Args:
            symbols: 股票代码列表
            period_days: 获取天数
            
        Returns:
            股票代码到DataFrame的映射
        """
        result = {}
        
        for symbol in symbols:
            try:
                df = await self.get_stock_data(symbol, period_days)
                result[symbol] = df
                logger.debug(f"成功获取 {symbol} 的数据，共 {len(df)} 条记录")
                
            except Exception as e:
                logger.error(f"获取股票 {symbol} 数据失败: {e}")
                result[symbol] = pd.DataFrame()  # 返回空DataFrame作为兜底
        
        logger.info(f"批量获取数据完成，共处理 {len(symbols)} 个股票")
        return result
    
    def get_data_source_status(self) -> Dict[str, bool]:
        """
        获取各数据源的状态
        
        Returns:
            数据源状态字典
        """
        return {
            'database': DATABASE_AVAILABLE and db_service is not None,
            'longport_api': self.longport_available
        }
    
    async def save_stock_data_to_database(self, symbol: str, df: pd.DataFrame, calculation_result: Dict = None):
        """将股票数据和计算结果保存到数据库，使用事务确保数据完整性"""
        try:
            if not DATABASE_AVAILABLE:
                logger.warning("数据库服务不可用，跳过数据保存")
                return
            
            # 导入数据库服务（避免循环导入）
            from database.service import db_service
            from database.models import StockData
            from decimal import Decimal
            
            # 检查数据是否需要更新
            if not await self._should_update_data(symbol, df, calculation_result):
                logger.info(f"跳过 {symbol} 数据更新：数据已是最新")
                return
            
            # 开启数据库事务
            with db_service.get_connection() as conn:
                try:
                    # 开始事务
                    conn.start_transaction()
                    logger.info(f"开始事务：保存 {symbol} 的数据")
                    
                    # 保存股票基础信息（仅在需要时）
                    if not await self._has_recent_basic_info(symbol):
                        stock_info = StockData(
                            symbol=symbol,
                            timestamp=datetime.now(),
                            data_type='basic_info',
                            name=symbol.replace('.US', ''),
                            exchange='NASDAQ',
                            sector='Technology'
                        )
                        await db_service.save_stock_data([stock_info])
                        logger.debug(f"{symbol} 更新了基础信息")
                    
                    # 第一步：保存K线数据
                    kline_data_list = []
                    for date, row in df.iterrows():
                        # 安全地转换数值，确保类型匹配
                        def safe_decimal(value, default=None):
                            if value is None or pd.isna(value):
                                return default
                            try:
                                return Decimal(str(float(value)))
                            except (ValueError, TypeError):
                                return default
                        
                        def safe_int(value, default=None):
                            if value is None or pd.isna(value):
                                return default
                            try:
                                return int(float(value))
                            except (ValueError, TypeError):
                                return default
                        
                        kline_data = StockData(
                            symbol=symbol,
                            timestamp=date,
                            data_type='kline',
                            # 只保存K线数据，确保类型正确
                            open=safe_decimal(row.get('Open')),
                            high=safe_decimal(row.get('High')),
                            low=safe_decimal(row.get('Low')),
                            close=safe_decimal(row.get('Close')),
                            volume=safe_int(row.get('Volume')),
                            turnover=safe_decimal(row.get('Turnover'))
                        )
                        kline_data_list.append(kline_data)
                    
                    # 批量保存K线数据
                    saved_count = await db_service.save_stock_data(kline_data_list)
                    logger.info(f"{symbol} 更新了 {saved_count} 条K线数据")
                    
                    # 第二步：如果有恐贪指标计算结果，更新到最新记录
                    if calculation_result and 'fear_greed_index' in calculation_result:
                        await self._update_latest_record_with_fear_greed(symbol, calculation_result, conn)
                    
                    # 提交事务
                    conn.commit()
                    logger.info(f"事务提交成功：{symbol} 数据保存完成")
                    
                except Exception as e:
                    # 回滚事务
                    conn.rollback()
                    logger.error(f"事务回滚：{symbol} 数据保存失败 - {e}")
                    raise
                    
        except Exception as e:
            logger.error(f"保存 {symbol} 数据到数据库失败: {e}")
            raise
    
    async def _update_latest_record_with_fear_greed(self, symbol: str, calculation_result: Dict, connection=None):
        """更新最新记录中的恐贪指标数据"""
        try:
            from database.service import db_service
            from database.models import StockData
            from decimal import Decimal
            
            # 安全地转换组件数据，确保类型匹配
            def safe_decimal(value, default=0):
                if value is None:
                    return Decimal(str(default))
                try:
                    # 处理numpy类型
                    if hasattr(value, 'item'):
                        value = value.item()
                    return Decimal(str(float(value)))
                except (ValueError, TypeError):
                    return Decimal(str(default))
            
            components = calculation_result.get('components', {})
            
            # 创建更新数据，确保所有字段都有正确的类型
            update_data = StockData(
                symbol=symbol,
                timestamp=datetime.now(),  # 使用当前时间作为更新时间
                data_type='kline',  # 保持为kline类型
                # 恐贪指标数据，确保类型正确
                fear_greed_index=safe_decimal(calculation_result.get('fear_greed_index')),
                sentiment=str(calculation_result.get('sentiment', '')),
                investment_advice=str(calculation_result.get('investment_advice', '')),
                rsi=safe_decimal(components.get('momentum', 0)),
                volatility=safe_decimal(components.get('volatility', 0)),
                volume_ratio=safe_decimal(components.get('volume', 0)),
                momentum_score=safe_decimal(components.get('momentum', 0)),
                volatility_score=safe_decimal(components.get('volatility', 0)),
                volume_score=safe_decimal(components.get('volume', 0)),
                put_call_score=safe_decimal(components.get('put_call_ratio', 0))
            )
            
            # 更新到数据库（支持事务）
            await db_service.save_stock_data([update_data], connection=connection)
            logger.info(f"{symbol} 更新了恐贪指标数据")
            
        except Exception as e:
            logger.error(f"更新 {symbol} 恐贪指标数据失败: {e}")
            raise
    
    async def _should_update_data(self, symbol: str, df: pd.DataFrame, calculation_result: Dict = None) -> bool:
        """检查是否需要更新数据"""
        try:
            if not DATABASE_AVAILABLE:
                return True
            
            from database.service import db_service
            
            # 检查K线数据是否需要更新
            if not df.empty:
                latest_date = df.index[-1]
                if await db_service.is_data_stale(symbol, 'kline', max_age_hours=1):
                    logger.debug(f"{symbol} K线数据需要更新")
                    return True
            
            # 检查恐贪指标是否需要更新
            if calculation_result and 'fear_greed_index' in calculation_result:
                if await db_service.is_data_stale(symbol, 'fear_greed', max_age_hours=2):
                    logger.debug(f"{symbol} 恐贪指标需要更新")
                    return True
            
            return False
            
        except Exception as e:
            logger.error(f"检查数据更新状态失败: {e}")
            return True  # 出错时默认更新
    
    async def _has_recent_basic_info(self, symbol: str) -> bool:
        """检查是否有最近的基础信息"""
        try:
            if not DATABASE_AVAILABLE:
                return False
            
            from database.service import db_service
            
            # 检查基础信息是否在24小时内更新过
            return not await db_service.is_data_stale(symbol, 'basic_info', max_age_hours=24)
            
        except Exception as e:
            logger.error(f"检查基础信息状态失败: {e}")
            return False

    async def check_calculation_status(self, symbol: str) -> Dict:
        """
        检查股票的计算状态
        
        Args:
            symbol: 股票代码
            
        Returns:
            计算状态信息
        """
        try:
            if not DATABASE_AVAILABLE:
                return {
                    'symbol': symbol,
                    'status': 'database_unavailable',
                    'has_data': False,
                    'latest_update': None,
                    'message': '数据库服务不可用'
                }
            
            from database.service import db_service
            
            # 检查是否有最新的恐贪指标数据
            latest_fear_greed = await db_service.get_latest_fear_greed(symbol)
            
            if latest_fear_greed and latest_fear_greed.fear_greed_index is not None:
                return {
                    'symbol': symbol,
                    'status': 'completed',
                    'has_data': True,
                    'latest_update': latest_fear_greed.updated_at,
                    'fear_greed_index': float(latest_fear_greed.fear_greed_index),
                    'message': '计算已完成'
                }
            else:
                # 检查是否有K线数据但没有恐贪指标
                kline_data = await db_service.get_stock_kline_data(symbol, 1)
                if kline_data:
                    return {
                        'symbol': symbol,
                        'status': 'calculating',
                        'has_data': False,
                        'latest_update': kline_data[0].updated_at if kline_data else None,
                        'message': 'K线数据已获取，恐贪指标计算中...'
                    }
                else:
                    return {
                        'symbol': symbol,
                        'status': 'no_data',
                        'has_data': False,
                        'latest_update': None,
                        'message': '暂无数据'
                    }
                    
        except Exception as e:
            logger.error(f"检查 {symbol} 计算状态失败: {e}")
            return {
                'symbol': symbol,
                'status': 'error',
                'has_data': False,
                'latest_update': None,
                'message': f'检查失败: {str(e)}'
            }
    
    async def wait_for_calculation_completion(self, symbol: str, timeout_seconds: int = 30) -> Dict:
        """
        等待计算完成
        
        Args:
            symbol: 股票代码
            timeout_seconds: 超时时间（秒）
            
        Returns:
            最终状态信息
        """
        import asyncio
        from datetime import datetime, timedelta
        
        start_time = datetime.now()
        timeout_time = start_time + timedelta(seconds=timeout_seconds)
        
        while datetime.now() < timeout_time:
            status = await self.check_calculation_status(symbol)
            
            if status['status'] == 'completed':
                logger.info(f"{symbol} 计算已完成")
                return status
            elif status['status'] == 'error':
                logger.error(f"{symbol} 计算出错: {status['message']}")
                return status
            
            # 等待1秒后再次检查
            await asyncio.sleep(1)
        
        # 超时
        logger.warning(f"{symbol} 计算超时（{timeout_seconds}秒）")
        return {
            'symbol': symbol,
            'status': 'timeout',
            'has_data': False,
            'latest_update': None,
            'message': f'计算超时（{timeout_seconds}秒）'
        }


# 创建全局实例
market_data_service = MarketDataService()
